#pragma once
#include <iostream>
#include <string>

#include <cstdio>
#include <stdarg.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <cstdlib>
#include <ctype.h>
using namespace std;

#define INFO 1
#define WARNING 2
#define ERROR 3
#define FATAL 4
#define DEBUG 5
#define BUFFSIZE 1024
#define DEFAULT_PATH "./log/"
#define LOG_NAME "log.txt"

class logger {
private:
    bool _isPrintInScreen;
    string _path;
    string _logName;
    string getLevelName(int level) const {
        switch (level)
        {
        case INFO:
            return "INFO";
        case WARNING:
            return "WARNING";
        case ERROR:
            return "ERROR";
        case FATAL:
            return "FATAL";
        case DEBUG:
            return "DEBUG";
        default:
            return "UNKNOWN";
        }
    }

    int openFile(const string& fileName) const {
        int fd = open(fileName.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
        if (fd < 0) {
            perror("open");
            exit(errno);
        }
        return fd;
    }
    //往等级文件写入
    void logInClassedFile(const string& logClassfiedFileName, const string& logmsg) const {
        int fd = openFile(logClassfiedFileName);
        write(fd, logmsg.c_str(), logmsg.size());
    }
    // 在目标目录中创建文件写入
    void logInFile(int logLevel, const string& logmsg) const {
        int fd = openFile(_logName);
        write(fd, logmsg.c_str(), logmsg.size());
        close(fd);
        // 通过日志等级创建不同的等级文件
        // 再在等级文件中写入
        string newName = _logName;
        newName += '.';
        string levelName = getLevelName(logLevel);
        for(const auto& c : levelName) {
            newName += tolower(c);
        }
        logInClassedFile(newName, logmsg);
    }
public:
    logger(const string& path = DEFAULT_PATH) : _path(path), _isPrintInScreen(true) {
        struct stat st;
        // 返回-1说明目标目录或文件不存在或出现错误
        if(stat(_path.c_str(), &st) == -1) {
            // 错误码为ENOENT表示目录不存在
            if(errno == ENOENT) {
                if(mkdir(_path.c_str(), 0755) == -1) {
                    perror("mkdir");
                    exit(errno);
                }
            }
            else {
                perror("stat");
                exit(errno);
            }
        }
        _logName = _path + LOG_NAME;
    }

    //设置往屏幕上打印
    void setScreenPrint() {
        _isPrintInScreen = true;
    }
    //设置不往屏幕上打印
    void unsetScreenPrint() {
        _isPrintInScreen = false;
    }
    void operator()(int logLevel, const char *format, ...)
    {
        char leftbuff[BUFFSIZE]{};
        // 获取时间
        time_t t = time(NULL);
        struct tm *tmp = localtime(&t);
        snprintf(leftbuff, sizeof leftbuff, "[%s][%d-%d-%d %d:%d:%d] ", getLevelName(logLevel).c_str(),
                 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
        char rightbuff[BUFFSIZE]{};
        va_list s;
        va_start(s, format);
        vsnprintf(rightbuff, sizeof rightbuff, format, s);
        va_end(s);
        // 格式由固定部分(等级、时间等)+ 自定义部分组成(内容)
        char logtxt[BUFFSIZE]{};
        snprintf(logtxt, sizeof logtxt, "%s%s\n", leftbuff, rightbuff);
        // 打印到显示器
        if(_isPrintInScreen) {
            printf("%s", logtxt);
        }
        // 写入文件
        //logInFile(logLevel, logtxt);
    }
};
